#pragma once
#include <memory>
#include <list>
#include <iostream>
#include <sstream>

class NotificationComposer;

class NotificationClient
{
public:
    virtual void Update( const NotificationComposer* composer ) = 0;
};

class NotificationComposer
{
public:
    NotificationComposer()
        :constructed( false )
    {}

    virtual ~NotificationComposer(){}
    virtual void Header() = 0;
    virtual void MetaData( const std::string& name, const std::string& val ) = 0;
    virtual void Footer() = 0;
    virtual void NewSection( const std::string& name ) = 0;
    virtual void EndSection() = 0;
    virtual void AppendMessage( const int errLevel, const std::string& message ) = 0;
    virtual void Reset(){ constructed = false; };
    virtual void Stream( std::ostream& out ) const = 0 ;
    bool Constructed() const { return constructed; }
    void StartConstruct() { constructed = true; }
    virtual void EndConstruct() {}
private:
    bool constructed;
};

class NotificationGenerator
{
public:
    virtual void Generate( NotificationComposer* composer ) = 0;
};

class NotificationManager
{
public:
    static NotificationManager* Instance()
    {
        try
        {
            static std::shared_ptr<NotificationManager> instance( new NotificationManager );
            return instance.get();
        }
        catch( std::bad_alloc& )
        {
            return NULL;
        }
    }

    void Detach( std::shared_ptr< NotificationClient > client )
    {
        for( auto it = m_clients.begin(); it != m_clients.end(); ++it )
        {
            if( client.get() == it->_Get() )
            {
                //we should delete it
                it = m_clients.erase( it );
            }
        }
    }

    void Attach( std::shared_ptr< NotificationClient > client )
    {
        m_clients.push_back( client ); //We only push back a weak pointer
    }

    void Notify( NotificationComposer* composer )
    {
        //Everything stored inside the composer, so we just make it as the param
        for( auto it = m_clients.begin(); it != m_clients.end(); ++it )
        {
            //Create a temp protection shared_ptr 
            std::shared_ptr< NotificationClient > client = it->lock(); 
            if( client )
            {
                client->Update( composer );
            }
            else
            {
                //We should delete it
                it = m_clients.erase( it );
            }
        }
    }

private:
    std::list< std::weak_ptr< NotificationClient > > m_clients;
};





